using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Controllers;
using System.Text;
using System.Text.RegularExpressions;

namespace RuoYi.Framework.Extensions;

/// <summary>
/// Http 拓展类
/// </summary>
[SuppressSniffer]
public static class HttpContextExtensions
{
    /// <summary>
    /// 获取 Action 特性
    /// </summary>
    /// <typeparam name="TAttribute"></typeparam>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    public static TAttribute GetMetadata<TAttribute>(this HttpContext httpContext)
        where TAttribute : class
    {
        return httpContext.GetEndpoint()?.Metadata?.GetMetadata<TAttribute>();
    }

    /// <summary>
    /// 获取 控制器/Action 描述器
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    public static ControllerActionDescriptor GetControllerActionDescriptor(this HttpContext httpContext)
    {
        return httpContext.GetEndpoint()?.Metadata?.FirstOrDefault(u => u is ControllerActionDescriptor) as ControllerActionDescriptor;
    }

    /// <summary>
    /// 设置规范化文档自动登录
    /// </summary>
    /// <param name="httpContext"></param>
    /// <param name="accessToken"></param>
    public static void SigninToSwagger(this HttpContext httpContext, string accessToken)
    {
        // 设置 Swagger 刷新自动授权
        httpContext.Response.Headers["access-token"] = accessToken;
    }

    /// <summary>
    /// 设置规范化文档退出登录
    /// </summary>
    /// <param name="httpContext"></param>
    public static void SignoutToSwagger(this HttpContext httpContext)
    {
        httpContext.Response.Headers["access-token"] = "invalid_token";
    }

    /// <summary>
    /// 设置响应头 Tokens
    /// </summary>
    /// <param name="httpContext"></param>
    /// <param name="accessToken"></param>
    /// <param name="refreshToken"></param>
    public static void SetTokensOfResponseHeaders(this HttpContext httpContext, string accessToken, string refreshToken = null)
    {
        httpContext.Response.Headers["access-token"] = accessToken;
        if (!string.IsNullOrWhiteSpace(refreshToken))
        {
            httpContext.Response.Headers["x-access-token"] = refreshToken;
        }
    }

    /// <summary>
    /// 获取本机 IPv4地址
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public static string GetLocalIpAddressToIPv4(this HttpContext context)
    {
        return context.Connection.LocalIpAddress?.MapToIPv4()?.ToString();
    }

    /// <summary>
    /// 获取本机 IPv6地址
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public static string GetLocalIpAddressToIPv6(this HttpContext context)
    {
        return context.Connection.LocalIpAddress?.MapToIPv6()?.ToString();
    }

    /// <summary>
    /// 获取远程 IPv4地址
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public static string GetRemoteIpAddressToIPv4(this HttpContext context)
    {
        return context.Connection.RemoteIpAddress?.MapToIPv4()?.ToString();
    }

    /// <summary>
    /// 获取远程 IPv6地址
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public static string GetRemoteIpAddressToIPv6(this HttpContext context)
    {
        return context.Connection.RemoteIpAddress?.MapToIPv6()?.ToString();
    }

    /// <summary>
    /// 获取完整请求地址
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    public static string GetRequestUrlAddress(this HttpRequest request)
    {
        return new StringBuilder()
                .Append(request.Scheme)
                .Append("://")
                .Append(request.Host)
                .Append(request.PathBase)
                .Append(request.Path)
                .Append(request.QueryString)
                .ToString();
    }

    /// <summary>
    /// 获取来源地址
    /// </summary>
    /// <param name="request"></param>
    /// <param name="refererHeaderKey"></param>
    /// <returns></returns>
    public static string GetRefererUrlAddress(this HttpRequest request, string refererHeaderKey = "Referer")
    {
        return request.Headers[refererHeaderKey].ToString();
    }

    /// <summary>
    /// 读取 Body 内容
    /// </summary>
    /// <param name="httpContext"></param>
    /// <remarks>需先在 Startup 的 Configure 中注册 app.EnableBuffering()</remarks>
    /// <returns></returns>
    public static async Task<string> ReadBodyContentAsync(this HttpContext httpContext)
    {
        if (httpContext == null) return default;
        return await httpContext.Request.ReadBodyContentAsync();
    }

    /// <summary>
    /// 读取 Body 内容
    /// </summary>
    /// <param name="request"></param>
    /// <remarks>需先在 Startup 的 Configure 中注册 app.EnableBuffering()</remarks>
    /// <returns></returns>
    public static async Task<string> ReadBodyContentAsync(this HttpRequest request)
    {
        request.Body.Seek(0, SeekOrigin.Begin);

        using var reader = new StreamReader(request.Body, Encoding.UTF8, true, 1024, true);
        var body = await reader.ReadToEndAsync();

        // 回到顶部
        request.Body.Seek(0, SeekOrigin.Begin);
        return body;
    }

    /// <summary>
    /// 将 <see cref="BadPageResult"/> 写入响应流中
    /// </summary>
    /// <param name="httpResponse"><see cref="HttpResponse"/></param>
    /// <param name="badPageResult"><see cref="BadPageResult"/></param>
    /// <param name="cancellationToken"><see cref="CancellationToken"/></param>
    /// <returns></returns>
    public static async ValueTask WriteAsync(this HttpResponse httpResponse, BadPageResult badPageResult, CancellationToken cancellationToken = default)
    {
        await httpResponse.Body.WriteAsync(badPageResult.ToByteArray(), cancellationToken);
    }

    /// <summary>
    /// 判断是否是 WebSocket 请求
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public static bool IsWebSocketRequest(this HttpContext context)
    {
        return context.WebSockets.IsWebSocketRequest || context.Request.Path == "/ws";
    }

    /// <summary>
    /// 设置内联文件下载响应头
    /// </summary>
    /// <param name="httpContext">HTTP上下文</param>
    /// <param name="fileName">文件名</param>
    public static void FileInlineHandle(this HttpContext httpContext, string fileName)
    {
        var encodeFilename = System.Web.HttpUtility.UrlEncode(fileName, Encoding.UTF8);
        httpContext.Response.Headers.Add("Content-Disposition", $"inline;filename={encodeFilename}");
    }

    /// <summary>
    /// 设置附件下载响应头
    /// </summary>
    /// <param name="httpContext">HTTP上下文</param>
    /// <param name="fileName">文件名</param>
    public static void FileAttachmentHandle(this HttpContext httpContext, string fileName)
    {
        var encodeFilename = System.Web.HttpUtility.UrlEncode(fileName, Encoding.UTF8);
        httpContext.Response.Headers.Add("Content-Disposition", $"attachment;filename={encodeFilename}");
    }

    /// <summary>
    /// 获取客户端首选语言
    /// </summary>
    /// <param name="httpContext">HTTP上下文</param>
    /// <returns>语言代码,默认返回zh-CN</returns>
    public static string GetLanguage(this HttpContext httpContext)
    {
        const string defaultLanguage = "zh-CN";
        var acceptLanguage = httpContext.Request.Headers["Accept-Language"].FirstOrDefault();

        return string.IsNullOrEmpty(acceptLanguage)
            ? defaultLanguage
            : acceptLanguage.Split(',')[0];
    }

    /// <summary>
    /// 判断是否为Ajax请求
    /// </summary>
    /// <param name="request">HTTP请求</param>
    /// <returns>是否为Ajax请求</returns>
    public static bool IsAjaxRequest(this HttpRequest request)
    {
        const string ajaxHeader = "XMLHttpRequest";
        return ajaxHeader.Equals(request.Headers["X-Requested-With"],
            StringComparison.OrdinalIgnoreCase);
    }

    /// <summary>
    /// 获取客户端IP地址
    /// </summary>
    /// <param name="context">HTTP上下文</param>
    /// <returns>客户端IP地址</returns>
    public static string GetClientIp(this HttpContext context)
    {
        const string localhost = "127.0.0.1";
        if (context == null) return string.Empty;

        // 尝试获取X-Forwarded-For头
        var ip = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();

        // 如果没有代理头,则获取远程IP
        if (string.IsNullOrEmpty(ip))
        {
            ip = context.Connection.RemoteIpAddress?.ToString();
        }

        // 处理特殊IP
        if (string.IsNullOrEmpty(ip) || ip.Contains("::1"))
        {
            return localhost;
        }

        // 清理IPv6格式
        ip = ip.Replace("::ffff:", localhost);

        // 移除端口号
        ip = Regex.Replace(ip, @":\d{1,5}$", "");

        // 验证IP格式
        var isValidIp = Regex.IsMatch(ip, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$") ||
                       Regex.IsMatch(ip, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?):\d{1,5}$");

        return isValidIp ? ip : localhost;
    }

    /// <summary>
    /// 获取User-Agent信息
    /// </summary>
    /// <param name="context">HTTP上下文</param>
    /// <returns>User-Agent字符串</returns>
    public static string GetUserAgent(this HttpContext context)
    {
        return context.Request.Headers["User-Agent"].ToString();
    }

    /// <summary>
    /// 获取用户权限声明值
    /// </summary>
    /// <param name="context">HTTP上下文</param>
    /// <param name="permissionsName">权限声明名称</param>
    /// <returns>权限值数组</returns>
    public static string[]? GetUserPermissions(this HttpContext context, string permissionsName)
    {
        return context.User.Claims
            .Where(x => x.Type == permissionsName)
            .Select(x => x.Value)
            .ToArray();
    }


}